package com.econ.powercloud.ems.aop;

import com.econ.powercloud.common.base.facade.enums.StatusType;
import com.econ.powercloud.common.base.facade.exception.CommonRuntimeException;
import com.econ.powercloud.ems.util.ExpressionUtils;
import com.econ.powercloud.ems.util.JoinPointUtils;
import com.econ.powercloud.ems.util.RedisCacheUtil;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @Author : zjf
 * @Description : 定时任务重试机制切面
 * @Date : 2022/11/1 8:46
 * @Modified by : zjf
 */
@Aspect
@Component
@Order(1)
@Slf4j
public class TimingRetryAspect {
    public static final Integer MAX_RETRY_NUMBER = 3;

    @Autowired
    private RedisCacheUtil redisCacheUtil;

    @Pointcut("@annotation(com.econ.powercloud.ems.aop.TimingRetryValid)")
    public void webLog() {
    }

    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature joinPointObject = (MethodSignature) joinPoint.getSignature();
        Method method = joinPointObject.getMethod();
        TimingRetryValid lock = method.getAnnotation(TimingRetryValid.class);
        String lockName = lock.lockName();
        if(!StringUtils.isEmpty(lock.lockExtraName())){
            Map<String, Object> params = JoinPointUtils.getParameterMap(joinPoint);
            Map<String, Object> filterParams = Maps.filterValues(params, it -> !(it instanceof HttpServletRequest || it instanceof HttpServletResponse));
            // EL表达式
            String redisExtraName = ExpressionUtils.parse(lock.lockExtraName(), filterParams);
            lockName = lockName + "-" + redisExtraName;
        }
        long timeOutSecond = lock.timeOutSecond();
        if (!redisCacheUtil.tryLock(lockName, timeOutSecond)){
            throw new CommonRuntimeException(StatusType.FAIL,joinPoint.getSignature() + "没获取redis锁,不再执行!");
        }
        for (int i=1; i<= MAX_RETRY_NUMBER; i++){
            try {
                return joinPoint.proceed();
            }catch (Exception e){
                log.error("方法{}:第{}次调用出现异常", joinPoint.getSignature(), i, e);
            }
        }
        throw new CommonRuntimeException(StatusType.FAIL,joinPoint.getSignature() + "方法重试三次失败,不在重试!");
    }

}
